--
-- Author: zhouzhanglin
-- Date: 2014-10-11 11:32:28
--
--不能点击--无点击事件--

--[[ 公共方法
创建方法 create

set:
设置文字方法 setLabelString
设置尺寸方法 setDimensions

get:
获得文字实际占用尺寸 getLabelSize
]]--

local RichLabel = class("RichLabel", require("ext.ui.Component"))   

RichLabel._fontName = nil
RichLabel._fontSize = nil
RichLabel._color = nil
RichLabel._containLayer = nil --装载layer
RichLabel._spriteArray = nil --精灵数组
RichLabel._textStr = nil
RichLabel._maxWidth = nil
RichLabel._maxHeight = nil

--播放状态 1 表示未开始 2 表示播放中 3 表示已经播放完毕 
RichLabel._labelStatus = 1

--[[-------------------
---Public Method-----
---------------------]]

--创建方法 
--[[
local params = {
fontName = "Arial",
fontSize = 30,
color = cc.c3b(255, 0, 0),
dimensions = cc.size(300, 200),
text = [color=f75d85 fontSize=20]hello world[/color],
}

text 目前支持参数
文字 
fontName  : font name
fontSize  : number
color : 十六进制

图片
image : "xxx.png"
]]--
function RichLabel:ctor(text,fontName,fontSize,color,dimensions)
    RichLabel.super.ctor(self)
    --如果text的格式指定字体则使用指定字体，否则使用默认字体
    --大小和颜色同理
    self._fontName   = fontName or "Arial" --默认字体
    self._fontSize    = fontSize or 30 --默认大小
    self._color        = color or cc.c3b(255, 255, 255) --默认白色
    self._dimensions = dimensions or cc.size(0, 0) --默认无限扩展，即沿着x轴往右扩展

    --装文字和图片精灵
    self._containLayer = display.newNode()
    self:addChild(self._containLayer)

    self:setLabelString(text)
end

--设置text
function RichLabel:setLabelString(text)
    if self._textStr == text then
        return --相同则忽略
    end

    if self._textStr then --删除之前的string
        self._spriteArray = nil
        self._containLayer:removeAllChildren()
    end

    self._labelStatus = 1 --未开始
    self:removeNodeEventListenersByEvent(cc.NODE_ENTER_FRAME_EVENT)
    self:unscheduleUpdate() --init

    self._textStr = text

    --转化好的数组
    local parseArray = self:_parseString(text)
    --将字符串拆分成一个个字符
    self:_formatString(parseArray)

    --创建精灵
    local spriteArray = self:_createSprite(parseArray)
    self._spriteArray = spriteArray

    self:_adjustPosition()
end

--设置尺寸
function RichLabel:setDimensions(dimensions)
    self._containLayer:setContentSize(dimensions)
    self._dimensions = dimensions

    self:_adjustPosition()
end

--获得label尺寸
function RichLabel:getLabelSize()
    local width = self._maxWidth or 0
    local height = self._maxHeight or 0
    return cc.size(width, height)
end

--是否在播放动画
function RichLabel:isRunningAmim()
    local isRunning = false
    if self._labelStatus == 2 then
        isRunning = true
    end
    return isRunning
end

--强制停止播放动画
function RichLabel:playEnded()
    print("play ended")
    self:removeNodeEventListenersByEvent(cc.NODE_ENTER_FRAME_EVENT)
    self:unscheduleUpdate()

    self._labelStatus = 3 --播放完毕
    for i, sprite in ipairs(self._spriteArray) do
        sprite:stopActionByTag(99)
        sprite:setOpacity(255)
    end
end

--播放fade in 动画
function RichLabel:playFadeInAnim(wordPerSec)
    local spriteArray = self._spriteArray

    if spriteArray then

        if self._labelStatus == 2 then --上一个动画播放中
            self:playEnded()
        end

        self._labelStatus = 2--播放中

        wordPerSec = wordPerSec or 15 --每秒多少个字

        local delay = 1 / wordPerSec

        do -- next action
            local curTime = 0

            local totalNum = #spriteArray

            if totalNum == 0 then
                self._labelStatus = 3 --播放完毕
                return
            end

            local totalTime = totalNum * delay
            local curIntIndex = 1

            --init
            for i, sprite in ipairs(spriteArray) do
                sprite:setOpacity(0)
            end

            local function updatePosition(dt)

                curTime = curTime + dt

                --这个类似动作里面的update的time参数
                local time = curTime / totalTime

                local fIndex = (totalNum - 1) * time + 1 --从1开始
                local index  = math.floor(fIndex)

                if index < totalNum then


                else --最后一个点
                    self._labelStatus = 3 --播放完毕
                    self:removeNodeEventListenersByEvent(cc.NODE_ENTER_FRAME_EVENT)
                    self:unscheduleUpdate()
                end

                if index >= curIntIndex then
                    for i = curIntIndex, index do
                        local sprite = spriteArray[i]

                        if sprite then
                            local action = cc.FadeIn:create(0.2)
                            action:setTag(99)
                            sprite:runAction(action)
                        else
                            print("Error: sprite not exist")
                        end
                    end

                    curIntIndex = index + 1
                end

            end

            self:removeNodeEventListenersByEvent(cc.NODE_ENTER_FRAME_EVENT)
            self:unscheduleUpdate()
            self:addNodeEventListener(cc.NODE_ENTER_FRAME_EVENT, updatePosition) 
            self:scheduleUpdate()
        end
    end
end


--获得每个精灵的尺寸
function RichLabel:_getSizeOfSprites(spriteArray)
    local widthArr = {} --宽度数组
    local heightArr = {} --高度数组

    --精灵的尺寸
    for i, sprite in ipairs(spriteArray) do
        -- local contentSize = sprite:getContentSize()
        local rect = sprite:getBoundingBox()
        widthArr[i] = rect.width
        heightArr[i] = rect.height
    end
    return widthArr, heightArr

end

--获得每个精灵的位置
function RichLabel:_getPointOfSprite(widthArr, heightArr, dimensions)
    local totalWidth = dimensions.width
    local totalHight = dimensions.height

    local maxWidth = 0
    local maxHeight = 0

    local spriteNum = #widthArr

    --从左往右，从上往下拓展
    local curX = 0 --当前x坐标偏移

    local curIndexX = 1 --当前横轴index
    local curIndexY = 1 --当前纵轴index

    local pointArrX = {} --每个精灵的x坐标

    local rowIndexArr = {} --行数组，以行为index储存精灵组
    local indexArrY = {} --每个精灵的行index

    --计算宽度，并自动换行
    for i, spriteWidth in ipairs(widthArr) do
        local nexX = curX + spriteWidth
        local pointX
        local rowIndex = curIndexY

        local halfWidth = spriteWidth * 0.5
        if nexX > totalWidth and totalWidth ~= 0 then --超出界限了
            pointX = halfWidth
            if curIndexX == 1 then --当前是第一个，
                curX = 0-- 重置x
            else --不是第一个，当前行已经不足容纳
                rowIndex = curIndexY + 1 --换行
                curX = spriteWidth
            end
            curIndexX = 1 --x坐标重置
            curIndexY = curIndexY + 1 --y坐标自增
        else
            pointX = curX + halfWidth --精灵坐标x
            curX = pointX + halfWidth --精灵最右侧坐标
            curIndexX = curIndexX + 1
        end
        pointArrX[i] = pointX --保存每个精灵的x坐标

        indexArrY[i] = rowIndex --保存每个精灵的行

        local tmpIndexArr = rowIndexArr[rowIndex]

        if not tmpIndexArr then --没有就创建
            tmpIndexArr = {}
            rowIndexArr[rowIndex] = tmpIndexArr
        end
        tmpIndexArr[#tmpIndexArr + 1] = i --保存相同行对应的精灵

        if curX > maxWidth then
            maxWidth = curX
        end
    end

    local curY = 0
    local rowHeightArr = {} --每一行的y坐标

    --计算每一行的高度
    for i, rowInfo in ipairs(rowIndexArr) do
        local rowHeight = 0
        for j, index in ipairs(rowInfo) do --计算最高的精灵
            local height = heightArr[index]
            if height > rowHeight then
                rowHeight = height
            end
        end
        local pointY = curY + rowHeight * 0.5 --当前行所有精灵的y坐标（正数，未取反）
        rowHeightArr[#rowHeightArr + 1] = - pointY --从左往右，从上到下扩展，所以是负数
        curY = curY + rowHeight --当前行的边缘坐标（正数）

        if curY > maxHeight then
            maxHeight = curY
        end
    end

    self._maxWidth = maxWidth
    self._maxHeight = maxHeight

    local pointArrY = {}

    for i = 1, spriteNum do
        local indexY = indexArrY[i] --y坐标是先读取精灵的行，然后再找出该行对应的坐标
        local pointY = rowHeightArr[indexY]
        pointArrY[i] = pointY
    end

    return pointArrX, pointArrY
end

--调整位置（设置文字和尺寸都会触发此方法）
function RichLabel:_adjustPosition()

    local spriteArray = self._spriteArray

    if not spriteArray then --还没创建
        return
    end

    --获得每个精灵的宽度和高度
    local widthArr, heightArr = self:_getSizeOfSprites(spriteArray)

    --获得每个精灵的坐标
    local pointArrX, pointArrY = self:_getPointOfSprite(widthArr, heightArr, self._dimensions)

    for i, sprite in ipairs(spriteArray) do
        sprite:setPosition(pointArrX[i], pointArrY[i])
    end
end

--创建精灵
function RichLabel:_createSprite(parseArray)
    local spriteArray = {}

    for i, dic in ipairs(parseArray) do
        local textArr = dic.textArray
        if #textArr > 0 then --创建文字
            local fontName = dic.fontName or self._fontName
            local fontSize = dic.fontSize or self._fontSize
            local color = dic.color or self._color
            for j, word in ipairs(textArr) do
                local label = cc.LabelTTF:create(word, fontName, fontSize)
                label:setColor(color)
                spriteArray[#spriteArray + 1] = label
                self._containLayer:addChild(label)
            end
        elseif dic.image then
            local sprite = display.newSprite(dic.image)
            local scale = dic.scale or 1
            sprite:setScale(scale)
            spriteArray[#spriteArray + 1] = sprite
            self._containLayer:addChild(sprite)
        else
            error("not define")
        end
    end

    return spriteArray
end

--将字符串转换成一个个字符
function RichLabel:_formatString(parseArray)
    for i,dic in ipairs(parseArray) do
        local text = dic.text
        if text then
            local textArr = self:_stringToChar(text)
            dic.textArray = textArr
        end
    end
end

--文字解析，按照顺序转换成数组，每个数组对应特定的标签
function RichLabel:_parseString(str)
    local clumpheadTab = {} -- 标签头
    --作用，取出所有格式为[xxxx]的标签头
    for w in string.gfind(str, "%b[]") do 
        if  string.sub(w,2,2) ~= "/" then-- 去尾
            table.insert(clumpheadTab, w)
        end
    end

    -- 解析标签
    local totalTab = {}
    for k,ns in pairs(clumpheadTab) do
        local tab = {}
        local tStr  
        -- 第一个等号前为块标签名
        string.gsub(ns, string.sub(ns, 2, #ns-1), function (w)
            local n = string.find(w, "=")
            if n then
                local temTab = self:_stringSplit(w, " ") -- 支持标签内嵌
                for k,pstr in pairs(temTab) do
                    local temtab1 = self:_stringSplit(pstr, "=")

                    local pname = temtab1[1]

                    if k == 1 then 
                        tStr = pname 
                    end -- 标签头

                    local js = temtab1[2]

                    local p = string.find(js, "[^%d.]")

                    if not p then 
                        js = tonumber(js) 
                    end

                    local switchState = {
                        ["color"]    = function()
                            tab["color"] = self:_convertColor(js)
                        end,
                    } --switch end

                    local fSwitch = switchState[pname] --switch 方法

                    --存在switch
                    if fSwitch then 
                        --目前只是颜色需要转换
                        local result = fSwitch() --执行function
                    else --没有枚举
                        tab[pname] = js     
                        return
                    end
                end
            end
        end)
        if tStr then
            -- 取出文本
            local beginFind,endFind = string.find(str, "%[%/"..tStr.."%]")
            local endNumber = beginFind-1
            local gs = string.sub(str, #ns+1, endNumber)
            if string.find(gs, "%[") then
                tab["text"] = gs
            else
                string.gsub(str, gs, function (w)
                    tab["text"] = w
                end)
            end
            -- 截掉已经解析的字符
            str = string.sub(str, endFind+1, #str)
            table.insert(totalTab, tab)
        end
    end
    -- 普通格式label显示
    if table.nums(clumpheadTab) == 0 then
        local ptab = {}
        ptab.text = str
        table.insert(totalTab, ptab)
    end
    return totalTab
end


--[[解析16进制颜色rgb值]]
function  RichLabel:_convertColor(xStr)
    local function toTen(v)
        return tonumber("0x" .. v)
    end

    local b = string.sub(xStr, -2, -1) 
    local g = string.sub(xStr, -4, -3) 
    local r = string.sub(xStr, -6, -5)

    local red = toTen(r) or self._color.r
    local green = toTen(g) or self._color.g
    local blue = toTen(b) or self._color.b
    return cc.c3b(red, green, blue)
end

-- string.split()
function RichLabel:_stringSplit(str, flag)
    local tab = {}
    while true do
        local n = string.find(str, flag)
        if n then
            local first = string.sub(str, 1, n-1) 
            str = string.sub(str, n+1, #str) 
            table.insert(tab, first)
        else
            table.insert(tab, str)
            break
        end
    end
    return tab
end

-- 拆分出单个字符
function RichLabel:_stringToChar(str)
    local list = {}
    local len = string.len(str)
    local i = 1 
    while i <= len do
        local c = string.byte(str, i)
        local shift = 1
        if c > 0 and c <= 127 then
            shift = 1
        elseif (c >= 192 and c <= 223) then
            shift = 2
        elseif (c >= 224 and c <= 239) then
            shift = 3
        elseif (c >= 240 and c <= 247) then
            shift = 4
        end
        local char = string.sub(str, i, i+shift-1)
        i = i + shift
        table.insert(list, char)
    end
    return list, len
end

return RichLabel